{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "4ba2396f",
   "metadata": {},
   "source": [
    "# Comparison of simple linear regression in Pytorch vs Neuromancer\n",
    "\n",
    "given data $y, x$ estimate $W, b$ in $\\, y = Wx + b$\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "af297282",
   "metadata": {},
   "source": [
    "### Install (Colab only)\n",
    "Skip this step when running locally."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6cd633cf",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install neuromancer"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a53dec3e",
   "metadata": {},
   "source": [
    "*Note: When running on Colab, one might encounter a pip dependency error with Lida 0.0.10. This can be ignored*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad0afc01",
   "metadata": {},
   "source": [
    "### Import"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "6ed78a03",
   "metadata": {},
   "outputs": [],
   "source": [
    "from neuromancer.constraint import variable\n",
    "import torch\n",
    "import torch.nn.functional as F"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d99711c8",
   "metadata": {},
   "source": [
    "### Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "9d8b5420",
   "metadata": {},
   "outputs": [],
   "source": [
    "# randomly generate the problem data\n",
    "torch.manual_seed(0)\n",
    "x_true = torch.arange(0.0, 1.0 + 0.1, 0.1)\n",
    "w_true = torch.tensor(0.5)\n",
    "b_true = torch.tensor(1.0)\n",
    "y_true = x_true * w_true + b_true"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f0257cc",
   "metadata": {},
   "source": [
    "### Pure Pytorch regression model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "e537f6e5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "w estimated to tensor([0.5000], requires_grad=True) true value is 0.5\n",
      "b estimated to tensor([1.0000], requires_grad=True) true value is 1.0\n"
     ]
    }
   ],
   "source": [
    "# randomly initialize parameters\n",
    "torch.manual_seed(0)\n",
    "w_1 = torch.randn(1, requires_grad=True)\n",
    "b_1 = torch.randn(1, requires_grad=True)\n",
    "\n",
    "# training\n",
    "opt = torch.optim.Adam([w_1, b_1])  # parameters of tensor model\n",
    "epochs = 10000\n",
    "losses = []\n",
    "for i in range(epochs):\n",
    "    opt.zero_grad()\n",
    "    y_torch = x_true * w_1 + b_1\n",
    "    loss = F.mse_loss(y_torch, y_true)\n",
    "    losses.append(loss.item())\n",
    "    loss.backward(retain_graph=True)\n",
    "    opt.step()\n",
    "\n",
    "print(f\"w estimated to {w_1} true value is {w_true}\")\n",
    "print(f\"b estimated to {b_1} true value is {b_true}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ffa5777",
   "metadata": {},
   "source": [
    "### Neuromancer variable model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "77310a37",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAApQAAAHzCAYAAACe1o1DAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABhnklEQVR4nO3deVhU9f4H8PcsMMPiwhLmAlqiprigJuACiktpCg5qgqOmpWVp6a9s9bZoi9XNylvevO2lMmCaGlczlzOokAhuqKkpRgpuIODGzsyc3x8qFxUQmYEzy/v1PD1PznLmjUm8/Xy/5xyZKIoiiIiIiIjqSS51ACIiIiKybSyURERERGQWFkoiIiIiMgsLJRERERGZhYWSiIiIiMzCQklEREREZmGhJCIiIiKzsFASERERkVlYKImIiIjILCyURERERGQWFkoiIiIiMgsLJRERERGZhYWSiIiIiMzCQklEREREZmGhJCIiIiKzsFASERERkVlYKImIiIjILCyURERERGQWFkoiIiIiMgsLJRERERGZhYWSiIiIiMzCQklEREREZmGhJCIiIiKzsFASERERkVlYKImIiIjILCyURERERGQWFkoiIiIiMgsLJRERERGZhYWSiIiIiMzCQklEREREZmGhJCIiIiKzsFASERERkVlYKImIiIjILCyURERERGQWFkoiIiIiMgsLJRERERGZhYWSiIiIiMyilDoAERER1a6ozICT+UUoN5jgrJSjnZcb3FT8EU7Wg38aiYiIrFBGzlXEpmYh8VgusgqKIVZ5TgbAz9MV4Z18MDHYDx1aNJEqJhEAQCaKonjnlxEREVFjyC4oxry1h5B0Ig8KuQxGU80/pm88H+rvjYVR3eDr6dqISYn+h4WSiIjISsTvzsJbCYdhMIm1FslbKeQyKOUyLIgMQEwfvwZMSFQ9FkoiIiIrsCQxA4s2Hzf7OC8+1BHPhnewQCKiuuNZ3kRERBKL351lkTIJAIs2H8fK3VkWORZRXbFQEhERSSi7oBhvJRy26DHfTDiM7IJiix6TqDYslERERBKat/YQDHexX7IuDCYR89YesugxiWrDQklERCSRjJyrSDqRd1cn4NSF0SQi6UQeTuRetehxiWrCQklERCSR2NQsKOSyGp83VZThzFdP48xXT8NUUVb5uLHkKk5/Phnnl78I0WSs9r0KuQwrdnEv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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# randomly initialize parameters\n",
    "torch.manual_seed(0)\n",
    "w_2 = torch.randn(1, requires_grad=True)\n",
    "b_2 = torch.randn(1, requires_grad=True)\n",
    "\n",
    "# create symbolic representation of the problem using Neuromancer variable\n",
    "var_x = variable(\"x\")\n",
    "var_w = variable(w_2,  display_name='w_2')\n",
    "var_b = variable(b_2,  display_name='b_2')\n",
    "var_y_est = var_x * var_w + var_b\n",
    "var_y_true = variable(y_true,  display_name='y_true')\n",
    "var_loss = F.mse_loss(var_y_est, var_y_true)\n",
    "# show computational graph of the loss function variable\n",
    "var_loss.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "38720c28",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "w estimated to tensor([0.5000], requires_grad=True) true value is 0.5\n",
      "b estimated to tensor([1.0000], requires_grad=True) true value is 1.0\n"
     ]
    }
   ],
   "source": [
    "# training\n",
    "opt_var = torch.optim.Adam(var_loss.parameters())  # parameters of variable model\n",
    "losses = []\n",
    "for i in range(epochs):\n",
    "    opt_var.zero_grad()\n",
    "    loss = var_loss({'x': x_true})\n",
    "    losses.append(loss.item())\n",
    "    loss.backward(retain_graph=True)\n",
    "    opt_var.step()\n",
    "\n",
    "print(f\"w estimated to {w_2} true value is {w_true}\")\n",
    "print(f\"b estimated to {b_2} true value is {b_true}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8e1f9ba0",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "neuromancer",
   "language": "python",
   "name": "neuromancer"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
